package com.xzz.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.xzz.domain.KillActivity;
import com.xzz.domain.KillCourse;
import com.xzz.dto.KillParamDto;
import com.xzz.dto.PreOrderDto;
import com.xzz.mapper.KillCourseMapper;
import com.xzz.service.IKillActivityService;
import com.xzz.service.IKillCourseService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.xzz.util.AssertUtil;
import com.xzz.util.CodeGenerateUtils;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xzz
 * @since 2022-09-21
 */
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {

    @Autowired
    private IKillActivityService killActivityService;

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public void addKillCourse(KillCourse killCourse) {
        //校验秒杀课程是否已经存在
        Wrapper<KillCourse> wrapper = new EntityWrapper<>();
        wrapper.eq("course_id",killCourse.getCourseId());
        wrapper.eq("activity_id",killCourse.getActivityId());
        KillCourse course = selectOne(wrapper);
        AssertUtil.isNull(course,"已经存在秒杀课程，请勿重复添加");

        //添加秒杀课程
        KillActivity killActivity = killActivityService.selectById(killCourse.getActivityId());
        killCourse.setKillLimit(1);
        killCourse.setStartTime(killActivity.getBeginTime());
        BeanUtils.copyProperties(killActivity,killCourse);
        /*killCourse.setEndTime(killActivity.getEndTime());
        killCourse.setPublishStatus(killActivity.getPublishStatus());
        killCourse.setTimeStr(killActivity.getTimeStr());*/
        insert(killCourse);

    }

    @Override
    public List<KillCourse> onlineAll() {
        //拿到所有的以avtivity:  开头的大key
        Set<Object> keys = redisTemplate.keys("avtivity:*");
        AssertUtil.isNotNull(keys,"不存在活动！！");
        List<KillCourse> courseArrayList= new ArrayList<>();
        // 根据每个大kay,获取大key下的所有秒杀商品
        for (Object key : keys) {
            List values = redisTemplate.opsForHash().values(key);
            // 把每一个活动的商品都追加到集合中
            courseArrayList.addAll(values);
        }
        return courseArrayList;
    }

    /**
     *大key是活动id，小key是秒杀的id
     */
    @Override
    public KillCourse onlineOne(Long killId, Long activityId) {
        String daKey = "avtivity:"+activityId;
        //通过大小key获取具体的一个课程
        return (KillCourse) redisTemplate.opsForHash().get(daKey,killId.toString());
    }

    /**
     * 执行秒杀
     */
    @Override
    public String kill(KillParamDto dto) {
        Long loginId = 3L;
        // 执行秒杀
        String repeatKey = loginId+":"+dto.getKillCourseId();
        //校验
        Object repeat = redisTemplate.opsForValue().get(repeatKey);
        AssertUtil.isNull(repeat,"你已经抢购过该课程，请参与其他课程秒杀！！");
        KillCourse killCourse = onlineOne(dto.getKillCourseId(),dto.getActivityId());
        AssertUtil.isNotNull(killCourse,"秒杀非法！！！");
        AssertUtil.isTrue(killCourse.isKilling(),"秒杀非法！！");

        // 执行秒杀，去扣减库存，redisson的信号量用于防止超卖
        String xiaoKey = killCourse.getId().toString();
        RSemaphore semaphore = redissonClient.getSemaphore(xiaoKey);
        int killCount = 1;
        boolean tryAcquire = semaphore.tryAcquire(killCount);
        AssertUtil.isTrue(tryAcquire,"下次再来！！");

        //  扣减成功，生成预创订单，存储redis
        String orderNo = CodeGenerateUtils.generateOrderSn(loginId);

        PreOrderDto preOrderDto = new PreOrderDto(
                orderNo,
                killCourse.getKillPrice(),
                1,
                loginId,
                killCourse.getCourseId()
        );
        redisTemplate.opsForValue().set(orderNo,preOrderDto);
        //这个只是为了防止重复秒杀
        redisTemplate.opsForValue().set(repeatKey,orderNo);
        return orderNo;

    }
}
